package com.atguigu.gulimall.ware.service.impl;

import com.atguigu.common.to.mq.WareStockLockTo;
import com.atguigu.common.to.ware.HasSkuStockTo;
import com.atguigu.common.to.ware.WareSkuLockRequestTo;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.ware.config.WareRabbitMQConfig;
import com.atguigu.gulimall.ware.dao.WareOrderTaskDao;
import com.atguigu.gulimall.ware.dao.WareSkuDao;
import com.atguigu.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.atguigu.gulimall.ware.entity.WareOrderTaskEntity;
import com.atguigu.gulimall.ware.entity.WareSkuEntity;
import com.atguigu.common.exception.ware.NoEnoughStockException;
import com.atguigu.gulimall.ware.feign.ProductFeignService;
import com.atguigu.gulimall.ware.service.WareOrderTaskDetailService;
import com.atguigu.gulimall.ware.service.WareOrderTaskService;
import com.atguigu.gulimall.ware.service.WareSkuService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Resource
    ProductFeignService productFeignService;

    @Autowired
    WareOrderTaskService orderTaskService;

    @Autowired
    WareOrderTaskDetailService wareOrderTaskDetailService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        // skuId: 4
        //wareId: 1
        QueryWrapper<WareSkuEntity> wrapper = new QueryWrapper<>();

        String skuId = params.getOrDefault("skuId", "").toString();
        if (!StringUtils.isEmpty(skuId)) {
            wrapper.eq("sku_id", skuId);
        }

        String wareId = params.getOrDefault("wareId", "").toString();
        if (!StringUtils.isEmpty(wareId)) {
            wrapper.eq("ware_id", wareId);
        }

        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                wrapper
        );

        return new PageUtils(page);
    }

    @Transactional
    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        // 判断是否存在当前库存

        int count = this.count(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if (count > 0) {
            // 存在库存，加库存
            this.baseMapper.addStock(skuId, wareId, skuNum);
            return;
        }
        WareSkuEntity wareSkuEntity = new WareSkuEntity();
        wareSkuEntity.setWareId(wareId);
        wareSkuEntity.setStock(skuNum);
        wareSkuEntity.setSkuId(skuId);
        wareSkuEntity.setStockLocked(0);
        // todo 远程查询sku名称，如果失败整个事务无需回滚
        // 1. 自己try/catch
        // todo 2. 另外的办法，高级篇中讲
        R info = productFeignService.info(skuId);
        if (info.isOk()) {
            Map<String, Object> skuInfo = (Map<String, Object>) info.get("skuInfo");
            wareSkuEntity.setSkuName(skuInfo.getOrDefault("skuName", "").toString());
        }
        this.save(wareSkuEntity);

    }

    /**
     * 获取当前sku 的库存信息
     *
     * @param skuIds skuId列表
     * @return sku库存信息
     */
    @Override
    public List<HasSkuStockTo> hasSkuStock(List<Long> skuIds) {
        List<HasSkuStockTo> hasSkuStockTos = skuIds.stream().map(skuId -> {
            Long count = this.baseMapper.selectStockInfo(skuId);
            HasSkuStockTo hasSkuStockTo = new HasSkuStockTo();
            hasSkuStockTo.setStock(count);
            hasSkuStockTo.setSkuId(skuId);
            hasSkuStockTo.setHasStock(count != null && count > 0);
            return hasSkuStockTo;
        }).collect(Collectors.toList());
        return hasSkuStockTos;
    }

    @Transactional(rollbackFor = NoEnoughStockException.class)
    @Override
    public void lockSkuItem(WareSkuLockRequestTo to) {
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(to.getOrderSn());
        wareOrderTaskEntity.setCreateTime(new Date());
        orderTaskService.save(wareOrderTaskEntity);

        List<WareSkuLockRequestTo.WareSkuLockItem> lockItems = to.getLockItems();
        for (WareSkuLockRequestTo.WareSkuLockItem lockItem : lockItems) {
            // 获取满足当前 sku 库存数量的 wareIds
            List<Long> wareIds = this.baseMapper.queryWareIdsBySkuIdAndSkuNum(lockItem.getSkuId(), lockItem.getCount());
            if (wareIds == null || wareIds.isEmpty()) {
                throw new NoEnoughStockException(lockItem.getSkuId(), lockItem.getSkuName(), lockItem.getCount());
            }
            boolean itemLockSuccess = false;
            // 遍历每个仓库尝试锁定库存，一旦有一个锁定成功，当前sku锁定库存就成功，退出当前循环
            for (Long wareId : wareIds) {
                Boolean locked = this.baseMapper.tryLockSku(lockItem.getSkuId(), wareId, lockItem.getCount());
                if (locked) {
                    WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
                    wareOrderTaskDetailEntity.setSkuId(lockItem.getSkuId());
                    wareOrderTaskDetailEntity.setSkuName(lockItem.getSkuName());
                    wareOrderTaskDetailEntity.setSkuNum(lockItem.getCount());
                    wareOrderTaskDetailEntity.setTaskId(wareOrderTaskEntity.getId());
                    wareOrderTaskDetailEntity.setWareId(wareId);
                    wareOrderTaskDetailEntity.setLockStatus(1);
                    wareOrderTaskDetailService.save(wareOrderTaskDetailEntity);

                    WareStockLockTo stockLockTo = new WareStockLockTo();
                    stockLockTo.setTaskId(wareOrderTaskEntity.getId());
                    stockLockTo.setOrderSn(to.getOrderSn());
                    WareStockLockTo.WareStockLockDetailTo wareStockLockDetailTo = new WareStockLockTo.WareStockLockDetailTo();
                    BeanUtils.copyProperties(wareOrderTaskDetailEntity, wareStockLockDetailTo);
                    stockLockTo.setDetail(wareStockLockDetailTo);

                    rabbitTemplate.convertAndSend(WareRabbitMQConfig.WARE_EVENT_EXCHANGE_NAME, WareRabbitMQConfig.WARE_LOCKED_ROUTE_KEY_NAME, stockLockTo);
                    itemLockSuccess = true;
                    break;
                }
            }
            if (!itemLockSuccess) {
                // 一个库存也没有锁定成功, 整体失败，抛出异常，回滚事务
                throw new NoEnoughStockException(lockItem.getSkuId(), lockItem.getSkuName(), lockItem.getCount());
            }
        }
    }

    @Override
    public boolean unlockStockBySkuIdAndWareId(Long skuId, Long wareId, Integer num) {
        return this.baseMapper.unlockStockBySkuIdAndWareId(skuId, wareId, num);
    }

}